package com.ganinfo.common.api.core;

import com.ganinfo.common.annotation.ApiMapping;
import org.springframework.context.ApplicationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 描述:api 注册中心
 * @author:ruikanwang
 * @date: 2018/1/19 0019
 * 注意：本内容仅限于新疆感知科技有限公司内部传阅,禁止外泄以及用于其他的商业目
 * Copyright © 2017-ganinfo. All rights reserved.
 */
public class ApiStore {
    private ApplicationContext applicationContext;
    /**
     * API 接口存储map
     */
    private HashMap<String, ApiRunnable> apiMap = new HashMap<String, ApiRunnable>();

    /**
     * @param applicationContext
     */
    public ApiStore(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    /**
     * 加载所有bean
     */
    public void loadApiFromSpringBeans() {
        String[] names = applicationContext.getBeanDefinitionNames();
        Class<?> type;
        for (String name : names) {
            type = applicationContext.getType(name);
            for (Method m : type.getDeclaredMethods()) {
            	// 通过反谢拿到APIMapping注解
                ApiMapping apiMapping = m.getAnnotation(ApiMapping.class);
                if (apiMapping != null) {
                    addApiItem(apiMapping, name, m);
                }
            }
        }
    }

    /**
     * 查找api
     * @param apiName
     * @return
     */
    public ApiRunnable findApiRunnable(String apiName) {
        return apiMap.get(apiName);
    }

    /**
     * 
     * 添加api <br/>
     * 
     * @param apiMapping
     *            api配置
     * @param beanName
     *            beanq在spring context中的名称
     * @param method
     */
    private void addApiItem(ApiMapping apiMapping, String beanName, Method method) {
    	ApiRunnable apiRun = new ApiRunnable();
        apiRun.apiName = apiMapping.value();
        apiRun.targetMethod = method;
        apiRun.targetName = beanName;
        apiMap.put(apiMapping.value(), apiRun);
    }

    public ApiRunnable findApiRunnable(String apiName, String version) {
        return (ApiRunnable) apiMap.get(apiName + "_" + version);
    }

    public List<ApiRunnable> findApiRunnables(String apiName) {
        if (apiName == null) {
            throw new IllegalArgumentException("api name must not null!");
        }
        List<ApiRunnable> list = new ArrayList<ApiRunnable>(20);
        for (ApiRunnable api : apiMap.values()) {
            if (api.apiName.equals(apiName)) {
                list.add(api);
            }
        }
        return list;
    }

    public List<ApiRunnable> getAll() {
        List<ApiRunnable> list = new ArrayList<ApiRunnable>(20);
        list.addAll(apiMap.values());
        Collections.sort(list, new Comparator<ApiRunnable>() {
            @Override
            public int compare(ApiRunnable o1, ApiRunnable o2) {
                return o1.getApiName().compareTo(o2.getApiName());
            }
        });
        return list;
    }

    public boolean containsApi(String apiName, String version) {
        return apiMap.containsKey(apiName + "_" + version);
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    
    /**
     * 描述:用于执行对应的API方法
     * @author:ruikanwang
     * @date: 2018/1/19 0019
     * 注意：本内容仅限于新疆感知科技有限公司内部传阅,禁止外泄以及用于其他的商业目
     * Copyright © 2017-ganinfo. All rights reserved.
     */
    public class ApiRunnable {
        /**
         * bit.api.user.getUser
         */
        String apiName;
        /**
         * ioc bean 名称
         */
        String targetName;
        /**
         * UserServiceImpl 实例
         */
        Object target;
        /**
         * 目标方法 getUser
         */
        Method targetMethod;

        /**
         * 多线程安全问题
         * @param args
         * @return
         * @throws IllegalAccessException
         * @throws IllegalArgumentException
         * @throws InvocationTargetException
         */
        public Object run(Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
			if (target == null) { // spring ioc 容器里面去服务Bean 比如GoodsServiceImpl
				target = applicationContext.getBean(targetName);
            }
            return targetMethod.invoke(target, args);
        }

        public Class<?>[] getParamTypes() {
            return targetMethod.getParameterTypes();
        }

        public String getApiName() {
            return apiName;
        }

        public String getTargetName() {
            return targetName;
        }

        public Object getTarget() {
            return target;
        }

        public Method getTargetMethod() {
            return targetMethod;
        }

    }

}
